/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.benchmark.security;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Random;
import java.util.TimeZone;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import cgl.narada.discovery.topics.Entity;
import cgl.narada.discovery.topics.messages.SignedTopicAdvertisement;
import cgl.narada.event.NBEvent;
import cgl.narada.event.TemplateInfo;
import cgl.narada.event.TemplateProfileAndSynopsisTypes;
import cgl.narada.event.impl.TemplateInfoImpl;
import cgl.narada.mgmt.common.CommandlineArguments;
import cgl.narada.service.ServiceException;
import cgl.narada.service.client.ClientService;
import cgl.narada.service.client.EventProducer;
import cgl.narada.service.client.SessionService;
import cgl.narada.service.qos.ProducerConstraints;
import cgl.narada.service.security.kmc.KMCClient;
import cgl.narada.service.security.kmc.SignedSecurityToken;
import cgl.narada.service.security.kmc.messages.SecureTopicKeyResponse;
import cgl.narada.service.security.securityprovider.CertificateManager;
import cgl.narada.service.security.securityprovider.CertificateUtil;
import cgl.narada.service.timer.ClockI;
import cgl.narada.service.timer.HRClock;
import cgl.narada.util.SystemInit;
import cgl.narada.util.SystemNotInitializedException;

public class PerfTesterSender {

	static Logger log = Logger.getLogger("PerfTesterSender");

	static String NB_HOME = "C:/Users/HARSHA~1/Desktop/NARADA~1.1/NARADA~1.1/";

	static {
		PropertyConfigurator.configure(NB_HOME + "/config/log4j.properties");
	}

	public static void main(String[] a) {
		SystemInit.init();

		try {
			NB_HOME = SystemInit.getInitData(SystemInit.SYS_NB_HOME);
			NB_HOME += NB_HOME.endsWith("/") ? "" : "/";
		} catch (SystemNotInitializedException e3) {
			e3.printStackTrace();
		}

		PerfTesterSender sender = new PerfTesterSender(a);
	}

	private String host = "gf3.ucs.indiana.edu";

	private String port = "5045";

	private String prot = "tcp";

	private String configFileLocation = NB_HOME
			+ "/config/ServiceConfiguration.txt";

	private String topicName = "encrypted";

	private String topicUUID;

	private PrivateKey c1_priv;

	int secureOut = 1;

	private String algo = null;

	private int keylen = -1;

	private int iters = 15;

	private EventProducer producer;

	SecureTopicKeyResponse resp;

	ProducerConstraints pc1, pc2, pc3;

	FileOutputStream fout = null;

	ClockI ts = HRClock.getClock();

	private boolean benchmark;

	public PerfTesterSender(String[] a) {

		CommandlineArguments cmdLine = new CommandlineArguments(a);

		if (cmdLine.getBooleanProperty("help", false)) {
			System.out.println("java " + PerfTesterSender.class.getName()
					+ " <options>");
			System.out.println(" --algo= algorithm to use (E.g. --algo=AES)");
			System.out.println(" --keylen= key length (E.g. --keylen=192)");
			System.out
					.println(" --host= broker host to use (E.g. --host=gf3.ucs.indiana.edu)");
			System.out
					.println(" --port= broker port to use (E.g. --port=5045)");
			System.out
					.println(" --prot= connection protocol to use (E.g. --prot=tcp)");
			System.out
					.println("\nE.g.: --algo=AES --keylen=256 --host=localhost --port=5045 --prot=tcp");
			System.out
					.println("--benchmark (default true): if False,then no measurement data is output ");
			System.out
					.println("--output (default /home/hgadgil/sds.out): output file for measurements");

			System.exit(0);
		}

		algo = cmdLine.getStringProperty("algo", "AES");
		keylen = cmdLine.getIntProperty("keylen", 192);
		host = cmdLine.getStringProperty("host", "localhost");
		port = cmdLine.getStringProperty("port", "5045");
		prot = cmdLine.getStringProperty("prot", "tcp");
		benchmark = cmdLine.getBooleanProperty("benchmark", true);
		String outputFile = cmdLine.getStringProperty("output",
				"/home/hgadgil/sdr.out");
		System.out.println("USING: alg(" + algo + ")\t keylen(" + keylen + ")");

		initTokens(NB_HOME + "/keystore/NBSecurityTest.keys");

		init();
		System.out.println("---------\nReady to publish...\n------------");
		try {
			System.in.read();
		} catch (IOException e) {
		}

		if (benchmark) {
			try {
				fout = new FileOutputStream("/home/hgadgil/sds.out");
			} catch (FileNotFoundException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				System.exit(-1);
			}
		}
		secureOut = 1;

		startSending(topicUUID, pc1);
		// startSending("XYZ", null);

		System.exit(0);

	}

	private void startSending(String topic, ProducerConstraints pc) {

		byte[] buffer;
		Random r = new Random();

		// System.out.println("100 - 1000");
		//
		// // SMALL 100 - 1000 (inc: 50)
		// for (int bufSize = 100; bufSize <= 1000; bufSize += 50) {
		// buffer = new byte[bufSize];
		//
		// for (int i = 0; i < iters; i++) {
		// r.nextBytes(buffer);
		// sendMessage(i, buffer, topic, pc);
		// }
		// }
		//
		// System.out.println("1000 - 50000");
		//
		// // BIG 1K - 50K (inc: 1K)
		// for (int bufSize = 1000; bufSize <= 50000; bufSize += 1000) {
		// buffer = new byte[bufSize];
		//
		// for (int i = 0; i < iters; i++) {
		// r.nextBytes(buffer);
		// sendMessage(i, buffer, topic, pc);
		// }
		// }

		System.out.println("0");
 
		// 1 byte buffers to remove start outlier
		int bufferSize = 1;
		buffer = new byte[bufferSize];

		for (int i = 0; i < iters; i++) {
			r.nextBytes(buffer);
			sendMessage(i, buffer, topic, pc);

		}
		System.out.println("0 - 16");

		// Power of 2 (1 - 16K) inc: 2^i
		for (int bufSize = 0; bufSize <= 16; bufSize += 1) {
			bufferSize = ((int) 1) << bufSize;
			buffer = new byte[bufferSize];

			for (int i = 0; i < iters; i++) {
				r.nextBytes(buffer);
				sendMessage(i, buffer, topic, pc);
			}
		}

		secureOut = 0;
		sendMessage(55, "hello".getBytes(), BenchmarkListener.BENCHMARK_COMMIT,
				null);
	}

	public void sendMessage(int i, byte[] msgBytes, String topicName,
			ProducerConstraints pc) {
		long tstamp = ts.getTimeMicroseconds();
		int bufferSize = msgBytes.length;

		try {
			NBEvent nbEvent = producer
					.generateEvent(TemplateProfileAndSynopsisTypes.STRING,
							topicName, msgBytes);
			if (secureOut == 1)
				producer.publishEvent(nbEvent, pc);
			else
				producer.publishEvent(nbEvent);
		} catch (ServiceException e) {
			log.error("", e);
		}
		if (benchmark) {

			try {
				fout
						.write(("i = " + i + ">" + bufferSize + ":" + tstamp + "\n")
								.getBytes());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		// System.out.println("i = " + i + ">" + bufferSize + ":" + tstamp);

		// Wait for delivery...:)
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
		}

	}

	private void init() {

		// IMP: Might conflict w/ other ppl's entity IDs Maintain constant for
		// now
		int entityId = (new Random()).nextInt();

		Properties prop = new Properties();

		prop.put("hostname", host);
		prop.put("portnum", port);

		log.info("SecureTopicsManager:- TCP @ " + host + ":" + port
				+ " EntityID[" + entityId + "]");

		try {
			ClientService clientService = SessionService.getClientService(
					entityId, configFileLocation);

			clientService.initializeBrokerCommunications(prop, prot);

			producer = clientService.createEventProducer();
			producer.setSuppressRedistributionToSource(true);
			producer.generateEventIdentifier(true);
			producer.setTemplateId(12345);
			producer.setDisableTimestamp(false);

			TemplateInfo ti = new TemplateInfoImpl(12345,
					TemplateProfileAndSynopsisTypes.STRING, topicUUID);

			pc1 = producer.createProducerConstraints(ti);
			pc1.setSendSecurely();
			Properties encryptProps1 = new Properties();
			encryptProps1.put(ProducerConstraints.SIGNING_ALGORITHM,
					"SHA1withRSA");

			pc1.setSecurityToken(resp.getSignedSecurityToken(), resp.getKey(),
					c1_priv, encryptProps1);

			pc2 = producer.createProducerConstraints(ti);
			pc2.setSendSecurely();
			Properties encryptProps2 = new Properties();
			encryptProps2.put(ProducerConstraints.SIGNING_ALGORITHM,
					"SHA256withRSAEncryption");

			pc2.setSecurityToken(resp.getSignedSecurityToken(), resp.getKey(),
					c1_priv, encryptProps2);

			pc3 = producer.createProducerConstraints(ti);
			pc3.setSendSecurely();
			Properties encryptProps3 = new Properties();
			encryptProps3.put(ProducerConstraints.SIGNING_ALGORITHM,
					"SHA384withRSAEncryption");

			pc3.setSecurityToken(resp.getSignedSecurityToken(), resp.getKey(),
					c1_priv, encryptProps3);

		} catch (ServiceException e) {
			log.error("", e);
		}
	}

	private void initTokens(String keystore) {
		CertificateManager certMan = new CertificateManager();
		certMan.init(keystore, null);

		PublicKey rootCA = CertificateUtil.getPublicKey(certMan,
				certMan.ROOT_CA_ALIAS);

		Certificate c1_cert = CertificateUtil.getCertificate(certMan, "c1");
		c1_priv = CertificateUtil.getPrivateKey(certMan, "c1");

		Calendar until = Calendar.getInstance(TimeZone.getTimeZone("GMT-5"));
		until.add(Calendar.HOUR, 1);

		Entity e = new Entity(9999,
				NB_HOME + "config/ServiceConfiguration.txt", "c1", c1_cert,
				c1_priv, rootCA, host, port, prot);

		if (e == null) {
			System.out.println("ERRRORRRR !!!!");
			return;
		}

		createTopic(e, topicName, TemplateProfileAndSynopsisTypes.STRING, until);

		KMCClient client = new KMCClient(c1_cert, c1_priv, rootCA, "/kmc/c1",
				NB_HOME + "config/ServiceConfiguration.txt", host, port, prot);

		// Get Signed Topic Ad
		SignedTopicAdvertisement sta = e.getSignedTopicAdvertisement(topicName);

		topicUUID = sta.getTopicAd().getTopicSynopsis();

		// Set the access control lists
		Hashtable pubs = new Hashtable();
		pubs.put(((X509Certificate) c1_cert).getSubjectDN().getName(), until);

		Hashtable subs = new Hashtable();
		subs.put(((X509Certificate) c1_cert).getSubjectDN().getName(), until);
		subs.put("CN=client2, OU=CGL, O=IU, L=Bloomington, C=US", until);
		subs.put("CN=client3, OU=DistLab, O=NYC, L=NewYork, C=US", until);

		// Ok, now register the topic and get a security token
		resp = client.registerTopic(pubs, subs, sta, c1_cert, until, algo,
				keylen, 5000);

		// Check the response
		SignedSecurityToken tok = resp.getSignedSecurityToken();
		System.out.println("VERIFICATION: " + tok.verify());
		System.out.println("Publish: "
				+ tok.getSecurityToken().getRights().hasPublishRight());
		System.out.println("Subscribe: "
				+ tok.getSecurityToken().getRights().hasSubscribeRight());
	}

	public boolean createTopic(Entity e, String topicName, int topicType,
			Calendar until) {

		if (e.sendTDNDiscoveryRequest(10000)) {
			System.out.println("Found TDN ! Proceeding to create TOPIC !!");

			java.util.Vector authorizedUsers = new java.util.Vector();
			authorizedUsers
					.add("CN=client2, OU=CGL, O=IU, L=Bloomington, C=US");
			authorizedUsers
					.add("CN=client3, OU=DistLab, O=NYC, L=NewYork, C=US");

			if (!e.createTopic("SELF", until, "Test Topic", topicType,
					topicName, authorizedUsers, 5000)) {
				System.err.println("Could not create topic ! Aborting...");
				return false;
			}

			System.out.println("TOPIC Created: UUID -> "
					+ e.getTopicUUID(topicName));
			return true;
		} else {
			System.out
					.println("NO TDN found within specified timeout period !!");
			return false;
		}
	}

}
